package cmd

import (
	"context"
	"encoding/json"
	"fmt"
	"os"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gcmd"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// 添加配置结构体
type IndexConfig struct {
	Keys    map[string]int `json:"keys"`
	Options struct {
		Unique     bool `json:"unique"`
		Background bool `json:"background"`
	} `json:"options"`
}

type CollectionConfig struct {
	Name    string        `json:"name"`
	Indexes []IndexConfig `json:"indexes"`
}

type MongoDBIndexes struct {
	Collections []CollectionConfig `json:"collections"`
}

var (
	// MongoDB 初始化命令
	MongoInit = &gcmd.Command{
		Name:  "mongo-init",
		Brief: "initialize mongodb collections and indexes",
		Description: `
        Initialize mongodb collections and create necessary indexes
        
        Options:
            -f, --force    Force rebuild all indexes 控制是否强制重建索引
            -c, --collection    Specify collection name to rebuild indexes (optional) 指定要处理的集合名称
        `,
		Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
			g.Log().Debug(ctx, "starting mongodb initialization")

			// 获取命令行参数
			forceRebuild := parser.GetOpt("force", false).Bool() || parser.GetOpt("f", false).Bool()
			specificCollection := parser.GetOpt("collection").String()
			if specificCollection == "" {
				specificCollection = parser.GetOpt("c").String()
			}
			if err := initMongoDB(ctx, forceRebuild, specificCollection); err != nil {
				g.Log().Fatalf(ctx, "mongodb initialization failed: %v", err)
				return err
			}

			g.Log().Debug(ctx, "mongodb initialization completed successfully")
			return nil
		},
	}
)

// initMongoDB 初始化MongoDB集合和索引
func initMongoDB(ctx context.Context, forceRebuild bool, specificCollection string) error {
	// 从配置文件获取MongoDB连接信息
	mongoUri := g.Cfg().MustGet(ctx, "mongoConf.mongoUrl").String()
	dbName := g.Cfg().MustGet(ctx, "mongoConf.mgoDbName").String()

	// 连接MongoDB
	clientOpts := options.Client().ApplyURI(mongoUri)
	client, err := mongo.Connect(ctx, clientOpts)
	if err != nil {
		return err
	}
	defer client.Disconnect(ctx)

	// 测试连接
	if err := client.Ping(ctx, nil); err != nil {
		return err
	}

	db := client.Database(dbName)

	// 读取索引配置文件
	configBytes, err := os.ReadFile("configs/mongodb_indexes.json")
	if err != nil {
		return fmt.Errorf("failed to read index config file: %v", err)
	}

	var indexConfig MongoDBIndexes
	if err := json.Unmarshal(configBytes, &indexConfig); err != nil {
		return fmt.Errorf("failed to parse index config: %v", err)
	}

	// 转换配置为 MongoDB 索引模型
	collections := make([]struct {
		name    string
		indexes []mongo.IndexModel
	}, len(indexConfig.Collections))

	for i, coll := range indexConfig.Collections {
		collections[i].name = coll.Name
		collections[i].indexes = make([]mongo.IndexModel, len(coll.Indexes))

		for j, idx := range coll.Indexes {
			// 转换 map[string]int 为 bson.D
			keys := bson.D{}
			for k, v := range idx.Keys {
				keys = append(keys, bson.E{Key: k, Value: v})
			}

			// 创建索引选项
			indexOptions := options.Index()
			if idx.Options.Unique {
				indexOptions.SetUnique(true)
			}
			if idx.Options.Background {
				indexOptions.SetBackground(true)
			}

			collections[i].indexes[j] = mongo.IndexModel{
				Keys:    keys,
				Options: indexOptions,
			}
		}
	}

	// 遍历创建索引
	for _, coll := range collections {
		// 如果指定了特定集合且不匹配当前集合，则跳过
		if specificCollection != "" && specificCollection != coll.name {
			continue
		}

		g.Log().Infof(ctx, "Checking indexes for collection: %s", coll.name)
		collection := db.Collection(coll.name)

		// 如果强制重建，先删除现有索引
		if forceRebuild {
			g.Log().Infof(ctx, "Force rebuilding indexes for collection: %s", coll.name)
			if _, err := collection.Indexes().DropAll(ctx); err != nil {
				return err
			}
		}

		// 获取现有索引
		cursor, err := collection.Indexes().List(ctx)
		if err != nil {
			return err
		}

		var existingIndexes []bson.M
		if err = cursor.All(ctx, &existingIndexes); err != nil {
			return err
		}

		// 检查并创建索引
		for _, index := range coll.indexes {
			if forceRebuild || shouldCreateIndex(index, existingIndexes) {
				g.Log().Infof(ctx, "Creating index for keys: %v in collection: %s", index.Keys, coll.name)
				_, err := collection.Indexes().CreateOne(ctx, index)
				if err != nil {
					return err
				}
			} else {
				g.Log().Infof(ctx, "Index already exists for keys: %v in collection: %s", index.Keys, coll.name)
			}
		}
	}

	return nil
}

// compareIndexKeys 比较两个索引键是否相同
func compareIndexKeys(keys1, keys2 bson.D) bool {
	if len(keys1) != len(keys2) {
		return false
	}

	// 创建map来存储keys1的键值对
	keyMap := make(map[string]int)
	for _, key := range keys1 {
		// Handle different numeric types
		switch v := key.Value.(type) {
		case int32:
			keyMap[key.Key] = int(v)
		case int64:
			keyMap[key.Key] = int(v)
		case int:
			keyMap[key.Key] = v
		default:
			return false
		}
	}

	// 比较keys2中的每个键值对是否在keys1中存在且值相同
	for _, key := range keys2 {
		value, exists := keyMap[key.Key]
		if !exists {
			return false
		}

		// Handle different numeric types for comparison
		switch v := key.Value.(type) {
		case int32:
			if value != int(v) {
				return false
			}
		case int64:
			if value != int(v) {
				return false
			}
		case int:
			if value != v {
				return false
			}
		default:
			return false
		}
	}

	return true
}

// shouldCreateIndex 检查是否需要创建索引
func shouldCreateIndex(newIndex mongo.IndexModel, existingIndexes []bson.M) bool {
	for _, existing := range existingIndexes {
		// 跳过_id索引
		if name, ok := existing["name"].(string); ok && name == "_id_" {
			continue
		}

		// Convert newIndex.Keys to bson.D
		newKeys, ok := newIndex.Keys.(bson.D)
		if !ok {
			continue // Skip if keys are in an unexpected format
		}

		if compareIndexKeys(newKeys, convertToBsonD(existing["key"])) {
			// 如果需要，这里可以添加更多的比较逻辑
			// 例如比较索引选项：unique, sparse等
			return false
		}
	}
	return true
}

// 添加一个新的辅助函数来处理类型转换
func convertToBsonD(key interface{}) bson.D {
	switch v := key.(type) {
	case bson.D:
		return v
	case bson.M:
		var d bson.D
		for k, val := range v {
			d = append(d, bson.E{Key: k, Value: val})
		}
		return d
	default:
		return bson.D{}
	}
}
